package com.sc.nft.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sc.nft.dao.*;
import com.sc.nft.dto.EcologyCastConfDao;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.AdminSalvageEventsPageVO;
import com.sc.nft.entity.vo.salvageEvents.*;
import com.sc.nft.enums.AalvageEventsLinkEnum;
import com.sc.nft.enums.NFTProductShowSalvagePrefectureEnum;
import com.sc.nft.enums.SalvageEventsModeEnum;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.AdminSalvageEventsDetailsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@RequiredArgsConstructor
@Service
public class AdminSalvageEventsDetailsServiceImpl implements AdminSalvageEventsDetailsService {

    private final SalvageEventsInfoDao salvageEventsInfoDao;
    private final SalvageEventsDetailsDao salvageEventsDetailsDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final UserSalvageEventsRecordsDao userSalvageEventsRecordsDao;
    private final EcologyCastConfDao ecologyCastConfDao;
    private final EquityPropsDao equityPropsDao;
    private final EcologyReforgeConfDao ecologyReforgeConfDao;

    @Override
    public Page<AdminSalvageEventsPageVO> getAdminSalvageEventsPage(String eventsName, Integer pageNo, Integer pageSize) {
        return salvageEventsInfoDao.getAdminSalvageEventsPage(eventsName, pageNo, pageSize);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void adminAddSalvageEvents(SalvageEventsInfo salvageEventsInfo, List<AdminAddAalvageEventsDetailsVO> collectionList) {
        Assert.isTrue(salvageEventsInfo.getBeginTime().before(salvageEventsInfo.getEndTime()), () -> new GlobalRunTimeException("开始时间不能大于结束时间!"));

        Assert.isTrue(salvageEventsInfo.getSumCount() > 0, () -> new GlobalRunTimeException("活动总量不能小于0!"));
        Assert.isTrue(salvageEventsInfo.getUserCount() > 0, () -> new GlobalRunTimeException("个人上限不能小于0!"));
        Assert.isTrue(salvageEventsInfo.getSumCount() >= salvageEventsInfo.getUserCount(), () -> new GlobalRunTimeException("活动总量不能小于个人上限!"));
        Assert.isTrue(salvageEventsInfo.getSumCountCoefficient() > 0, () -> new GlobalRunTimeException("分解总量系数不能小于0!"));
        Assert.isFalse(salvageEventsInfo.getSumCountCoefficient() > 1 && salvageEventsInfo.getSingleCount() > 1, () -> new GlobalRunTimeException("单次上线不能大于1"));
        Assert.isTrue(CollectionUtil.isNotEmpty(collectionList), () -> new GlobalRunTimeException("关联藏品不能为空"));
        List<Long> collectIdList = collectionList.stream().map(AdminAddAalvageEventsDetailsVO::getCollectionId).distinct().collect(Collectors.toList());
        Assert.isTrue(collectionList.size() == collectIdList.size(), () -> new GlobalRunTimeException("关联藏品重复"));
        List<DigitalCollection> digitalCollectionList = digitalCollectionDao.getByIds(collectIdList);
        Assert.isTrue(CollectionUtil.isNotEmpty(digitalCollectionList), () -> new GlobalRunTimeException("藏品不存在"));
        Assert.isTrue(collectIdList.size() == digitalCollectionList.size(), () -> new GlobalRunTimeException("藏品不存在"));
        Map<Long, DigitalCollection> digitalCollectionMap = digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity()));

        salvageEventsInfo.setUsableCount(salvageEventsInfo.getSumCount());
        salvageEventsInfo.insert();

        for (AdminAddAalvageEventsDetailsVO adminAddAalvageEventsDetailsVO : collectionList) {
            DigitalCollection collection = digitalCollectionMap.get(adminAddAalvageEventsDetailsVO.getCollectionId());
            BigDecimal totalProportion = BigDecimal.ZERO;
            for (AdminAddAalvageEventsRewardDetailsVO adminAddAalvageEventsRewardDetailsVO : adminAddAalvageEventsDetailsVO.getRewardDetailsList()) {
                Assert.isTrue(adminAddAalvageEventsRewardDetailsVO.getLinkType().getEnableInsert(), () -> new GlobalRunTimeException("关联类型不能为空"));
                SalvageEventsDetails salvageEventsDetails = new SalvageEventsDetails();
                salvageEventsDetails.setSalvageEventsId(salvageEventsInfo.getId());
                salvageEventsDetails.setCollectionId(collection.getId());
                salvageEventsDetails.setCollectionName(collection.getFirstTitle());
                salvageEventsDetails.setCollectionImage(collection.getCreationContent());
                salvageEventsDetails.setLinkType(adminAddAalvageEventsRewardDetailsVO.getLinkType());
                salvageEventsDetails.setLinkName(adminAddAalvageEventsRewardDetailsVO.getLinkName());
                salvageEventsDetails.setLinkCount(adminAddAalvageEventsRewardDetailsVO.getLinkCount());
                if (adminAddAalvageEventsRewardDetailsVO.getLinkType() == AalvageEventsLinkEnum.PROPS) {
                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getLinkId(), () -> new GlobalRunTimeException("虚拟道具id不能为空!"));
                    EquityProps equityProps = equityPropsDao.getById(adminAddAalvageEventsRewardDetailsVO.getLinkId());
                    Assert.notNull(equityProps, () -> new GlobalRunTimeException("未找到虚拟道具!"));
                    salvageEventsDetails.setLinkImg(equityProps.getCreationContent());
                    salvageEventsDetails.setLinkId(adminAddAalvageEventsRewardDetailsVO.getLinkId());
                } else if (adminAddAalvageEventsRewardDetailsVO.getLinkType() == AalvageEventsLinkEnum.UPPER_CASTING_LIMIT) {
                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getLinkId(), () -> new GlobalRunTimeException("铸造的藏品id不能为空!"));
                    EcologyCastConf ecologyCastConf = ecologyCastConfDao.getOneByCollectionId(adminAddAalvageEventsRewardDetailsVO.getLinkId());
                    Assert.notNull(ecologyCastConf, () -> new GlobalRunTimeException("铸造配置不存在!"));
                    salvageEventsDetails.setLinkName(StrUtil.format("{}铸造上限", ecologyCastConf.getCollectionName()));

                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getLinkId(), () -> new GlobalRunTimeException("权益道具id不能为空!"));
//                    Assert.notBlank(adminAddAalvageEventsRewardDetailsVO.getLinkImg(),()->  new GlobalRunTimeException("权益道具图片不能为空!"));

                    salvageEventsDetails.setLinkImg(adminAddAalvageEventsRewardDetailsVO.getLinkImg());
                    salvageEventsDetails.setLinkId(adminAddAalvageEventsRewardDetailsVO.getLinkId());
                } else if (adminAddAalvageEventsRewardDetailsVO.getLinkType() == AalvageEventsLinkEnum.UPPER_REFORGE_CASTING_LIMIT) {
                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getLinkId(), () -> new GlobalRunTimeException("铸造的藏品id不能为空!"));


                    EcologyReforgeConf ecologyReforgeConf = ecologyReforgeConfDao.getOneByCollectionId(adminAddAalvageEventsRewardDetailsVO.getLinkId());
                    Assert.notNull(ecologyReforgeConf, () -> new GlobalRunTimeException("重铸配置不存在!"));
                    salvageEventsDetails.setLinkName(StrUtil.format("{}重铸上限", ecologyReforgeConf.getCollectionName()));

                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getLinkId(), () -> new GlobalRunTimeException("权益道具id不能为空!"));

                    salvageEventsDetails.setLinkImg(adminAddAalvageEventsRewardDetailsVO.getLinkImg());
                    salvageEventsDetails.setLinkId(adminAddAalvageEventsRewardDetailsVO.getLinkId());
                } else {
                    salvageEventsDetails.setLinkImg(adminAddAalvageEventsRewardDetailsVO.getLinkType().getImg());
                }

                //region 分解模式
                salvageEventsDetails.setSalvageMode(adminAddAalvageEventsDetailsVO.getSalvageMode());
                if (Objects.equals(adminAddAalvageEventsDetailsVO.getSalvageMode(), SalvageEventsModeEnum.PROPORTION)) {
                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getSalvageProportion(), () -> new GlobalRunTimeException("分解比例不能为空"));
                    Assert.isTrue(adminAddAalvageEventsRewardDetailsVO.getSalvageProportion().compareTo(BigDecimal.ZERO) > 0, () -> new GlobalRunTimeException("分解比例必须大于0"));
                    Assert.notNull(adminAddAalvageEventsRewardDetailsVO.getSalvageTotalNum(), () -> new GlobalRunTimeException("分解总次数不能为空"));
                    Assert.isTrue(adminAddAalvageEventsRewardDetailsVO.getSalvageTotalNum() > 0, () -> new GlobalRunTimeException("分解总次数必须大于0"));
                    salvageEventsDetails.setSalvageProportion(adminAddAalvageEventsRewardDetailsVO.getSalvageProportion());
                    salvageEventsDetails.setSalvageTotalNum(adminAddAalvageEventsRewardDetailsVO.getSalvageTotalNum());
                    salvageEventsDetails.setSalvageUsableNum(adminAddAalvageEventsRewardDetailsVO.getSalvageTotalNum());
                    totalProportion = totalProportion.add(adminAddAalvageEventsRewardDetailsVO.getSalvageProportion());
                }
                //endregion

                salvageEventsDetails.insert();
            }
            //region 分解模式 比例模式总概率之和必须为100%
            Assert.isTrue(!Objects.equals(adminAddAalvageEventsDetailsVO.getSalvageMode(), SalvageEventsModeEnum.PROPORTION) || totalProportion.compareTo(BigDecimal.ONE) == 0, () -> new GlobalRunTimeException("概率之和必须为100%"));
            //endregion
        }
    }

    @Override
    public GetAdminDetailsByIdVO getAdminDetailsById(Long id) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在!"));
        GetAdminDetailsByIdVO detailsVO = BeanUtil.copyProperties(salvageEvents, GetAdminDetailsByIdVO.class);
        detailsVO.setCollectionDetailsList(salvageEventsDetailsDao.getAdminDetailsBysalvageEventsId(id));
        return detailsVO;
    }

    @Override
    public void updateIsListing(Long id, Boolean isListing) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在!"));
        Assert.isTrue(salvageEventsInfoDao.updateIsListingById(id, isListing), () -> new GlobalRunTimeException("修改失败!"));
    }

    @Override
    public void updateSort(Long id, Integer sort) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在!"));
        Assert.isTrue(salvageEventsInfoDao.updateSort(id, sort), () -> new GlobalRunTimeException("修改失败!"));
    }

    @Override
    public Page<AdminPageSalvageEventsRecordsVO> gtPageSalvageEventsRecords(String userTel, String eventsName, Integer pageNo, Integer pageSize) {
        return userSalvageEventsRecordsDao.gtPageSalvageEventsRecords(userTel, eventsName, pageNo, pageSize);
    }

    @Override
    public void delById(Long id) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在!"));
        Assert.isFalse(salvageEvents.getIsListing(), () -> new GlobalRunTimeException("分解活动已上架,不能删除!"));
        Assert.isTrue(salvageEventsInfoDao.delById(id), () -> new GlobalRunTimeException("删除失败!"));
    }

    @Override
    public void updateInfoById(Long id, String coverImage, String regularImage, Integer sumCount, Integer userCount, NFTProductShowSalvagePrefectureEnum showSalvagePrefecture, Boolean consumeConsensus) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在!"));

        Integer usableCount = userSalvageEventsRecordsDao.getSumCountById(id);
        Assert.isTrue(sumCount >= usableCount, () -> new GlobalRunTimeException("活动总量不能小于已分解数量!"));
        Assert.isFalse(Objects.nonNull(consumeConsensus)
                && !Objects.equals(salvageEvents.getConsumeConsensus(), consumeConsensus)
                && salvageEvents.getIsListing(), () -> new GlobalRunTimeException("已上架，不能修改是否消耗共识度"));
        Assert.isTrue(salvageEventsInfoDao.updateInfoById(id, coverImage, regularImage, sumCount, sumCount - usableCount, userCount, showSalvagePrefecture, consumeConsensus), () -> new GlobalRunTimeException("修改失败!"));
    }

    @Override
    public SalvageRewardsVO getRewards(Long id, Long collectionId) {
        List<SalvageEventsDetails> salvageEventsDetailsList = salvageEventsDetailsDao.getListByCollectionId(id, collectionId);
        Assert.isTrue(CollectionUtil.isNotEmpty(salvageEventsDetailsList), () -> new GlobalRunTimeException("奖励不存在"));
        SalvageRewardsVO salvageRewardsVO = new SalvageRewardsVO();
        salvageRewardsVO.setSalvageMode(salvageEventsDetailsList.get(0).getSalvageMode());
        List<SalvageRewardsDetailVO> rewardsList = new ArrayList<>();
        for (SalvageEventsDetails salvageEventsDetails : salvageEventsDetailsList) {
            SalvageRewardsDetailVO salvageRewardsDetailVO = new SalvageRewardsDetailVO();
            salvageRewardsDetailVO.setLinkType(salvageEventsDetails.getLinkType());
            salvageRewardsDetailVO.setLinkTypeName(salvageEventsDetails.getLinkType().getDescription());
            salvageRewardsDetailVO.setLinkName(salvageEventsDetails.getLinkName());
            salvageRewardsDetailVO.setLinkImg(salvageEventsDetails.getLinkImg());
            salvageRewardsDetailVO.setLinkId(salvageEventsDetails.getLinkId());
            salvageRewardsDetailVO.setLinkCount(salvageEventsDetails.getLinkCount());
            salvageRewardsDetailVO.setSalvageProportion(salvageEventsDetails.getSalvageProportion());
            salvageRewardsDetailVO.setSalvageTotalNum(salvageEventsDetails.getSalvageTotalNum());
            salvageRewardsDetailVO.setSalvageUsableNum(salvageEventsDetails.getSalvageUsableNum());
            rewardsList.add(salvageRewardsDetailVO);
        }
        salvageRewardsVO.setRewardsList(rewardsList);
        return salvageRewardsVO;
    }

}
